package chances.epg.search.searcher;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.core.KeywordAnalyzer;
import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.FieldCacheRangeFilter;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.TopFieldCollector;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IOContext;
import org.apache.lucene.store.NIOFSDirectory;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;
import org.slf4j.Logger;

import chances.epg.commons.log.EpgLogFactory;
import chances.epg.commons.utils.CommonUtils;
import chances.epg.search.AnalyzerFactory;
import chances.epg.search.IndexField;
import chances.epg.search.LucenceVersion;

public class SearchHelper {
	private static final Logger logger = EpgLogFactory.getSearchLogger();
	private Version DEFAULT_VERSION = LucenceVersion.VERSION;
	
	private IndexSearcher searcher = null;
	
	public SearchHelper(IndexSearcher searcher) {
		this.searcher = searcher;
	}
	

	/**
	 * 按导演和演员检索内容.
	 * 
	 * @param request
	 * @return
	 * @throws ParseException
	 */
	public SearchResult searchByPersons(SearchRequest request) throws ParseException {
		String[] fieldList = new String[] { IndexField.FIELD_ACTOR, IndexField.FIELD_DIRECTOR,
				IndexField.FIELD_COMPERE };
		String[] querieList = new String[] { request.getCondition(), request.getCondition(), request.getCondition() };
		BooleanClause.Occur[] occur = new BooleanClause.Occur[] { BooleanClause.Occur.SHOULD,
				BooleanClause.Occur.SHOULD, BooleanClause.Occur.SHOULD };

		//按人员查询不排除当前指定的内容
		request.setExcludeSelf(false);
		return this.search(fieldList, querieList, occur, request);
	}

	/**
	 * 按标签检索内容.
	 * 
	 * @param request
	 * @return
	 * @throws ParseException
	 */
	public SearchResult searchByTags(SearchRequest request) throws ParseException {
		String[] fieldList = new String[] { IndexField.FIELD_BASE_TAGS, IndexField.FIELD_OP_TAGS };
		String[] querieList = new String[] { request.getCondition(), request.getCondition() };
		BooleanClause.Occur[] occur = new BooleanClause.Occur[] { BooleanClause.Occur.SHOULD,
				BooleanClause.Occur.SHOULD };
		return this.search(fieldList, querieList, occur, request);
	}
	
	@SuppressWarnings("unused")
	private static final int ORDER_BYID = 1;
	private static final int ORDER_BYSCORE = 2;

	public SearchResult filter(String type, String tag, String region, String year,boolean early, int orderBy, int curPage,
			int size, boolean isRandom, int randomSize) {
		SearchResult result = new SearchResult();
		
		//Map<String, Analyzer> analyzerPerField = new HashMap<String, Analyzer>();
		String[] fieldList = new String[]{IndexField.FIELD_BASE_TYPE};
		String[] querieList = new String[]{type};
		BooleanClause.Occur[] occur = new BooleanClause.Occur[] { BooleanClause.Occur.MUST};
		
		if(StringUtils.isNotBlank(tag)){
			fieldList = (String[]) ArrayUtils.add(fieldList, IndexField.FIELD_BASE_TAGS);
			querieList = (String[]) ArrayUtils.add(querieList, tag);
			occur = (BooleanClause.Occur[]) ArrayUtils.add(occur, BooleanClause.Occur.MUST);
		}
		
		if(StringUtils.isNotBlank(region)){
			fieldList = (String[]) ArrayUtils.add(fieldList, IndexField.FIELD_REGION);
			querieList = (String[]) ArrayUtils.add(querieList, region);
			occur = (BooleanClause.Occur[]) ArrayUtils.add(occur, BooleanClause.Occur.MUST);
		}
		
		Filter filter = null;
		if(early){
			filter = FieldCacheRangeFilter.newStringRange(IndexField.FIELD_YEAR, "0", year, false, false);  
		}else{
			if(StringUtils.isNotBlank(year)){
				fieldList = (String[]) ArrayUtils.add(fieldList, IndexField.FIELD_YEAR);
				querieList = (String[]) ArrayUtils.add(querieList, year);
				occur = (BooleanClause.Occur[]) ArrayUtils.add(occur, BooleanClause.Occur.MUST);
			}
		}
		
		PerFieldAnalyzerWrapper analyzer = new PerFieldAnalyzerWrapper(this.getSplitAnalyzer());
		
		//按最新排序
		SortField sortFieldId = new SortField(IndexField.FIELD_ID, SortField.Type.LONG, true);	
		Sort sort = new Sort(new SortField[] {sortFieldId});
		
		//按最热排序
		if(orderBy == ORDER_BYSCORE) {
			SortField sortFieldMd = new SortField(IndexField.FIELD_SCORE, SortField.Type.FLOAT, true);
			sort = new Sort(new SortField[] {sortFieldMd});
		}
		
		try {
			Query query = MultiFieldQueryParser.parse(DEFAULT_VERSION, querieList, fieldList, occur, analyzer);
			/*
			TopDocs topDocs = searcher.search(query, 2000, sort);
			ScoreDoc[] hits = topDocs.scoreDocs;
			result.setTotalCount(hits.length);

			int start = (curPage - 1) * size;
			if (hits.length < start) {
				logger.info("start max than hits length,return empty result.");
				return result;
			}

			int end = curPage * size;
			if (hits.length < end) {
				end = hits.length;
			}

			for (int i = start; i < end; i++) {
				Document hitDoc = searcher.doc(hits[i].doc);
				result.getResult().add(hitDoc.get(IndexField.FIELD_CONTENT));
			}
			*/
			if(isRandom) {
				result = this.searchInPage(query, sort, curPage, randomSize,filter);
				result.setResult(getRandomResult(size, result));
			} else {
				result = this.searchInPage(query, sort, curPage, size,filter);
			}
		} catch (IOException | ParseException ex) {
			logger.error("searchInPage error.", ex);
		}
		return result;
	}

	private List<Object> getRandomResult(int size, SearchResult result) {
		List<Object> list = result.getResult();
		List<Object> randomResult = new ArrayList<Object>();
		int[] randomSequence = CommonUtils.getRandomSequence(list.size());
		for (int i = 0; i < size; i++) {
			randomResult.add(list.get(randomSequence[i]));
		}
		return randomResult;
	}

	/**
	 * 按自定义条件鉴权内容.
	 * 
	 * @param request
	 * @return
	 */
	public SearchResult searchByCondition(SearchRequest request) {
		throw new UnsupportedOperationException();
	}

	/**
	 * 检索.
	 * 
	 * @param query
	 * @param size
	 * @return
	 * @throws ParseException
	 * @throws IOException
	 */
	private SearchResult search(String[] fieldList, String[] querieList, BooleanClause.Occur[] occur,
			SearchRequest request) throws ParseException {
		Map<String, Analyzer> analyzerPerField = new HashMap<String, Analyzer>();

		if (StringUtils.isNotEmpty(request.getType())) {
			analyzerPerField.put(IndexField.FIELD_BASE_TYPE, new KeywordAnalyzer());
			fieldList = (String[]) ArrayUtils.add(fieldList, IndexField.FIELD_BASE_TYPE);
			querieList = (String[]) ArrayUtils.add(querieList, request.getType());
			occur = (BooleanClause.Occur[]) ArrayUtils.add(occur, BooleanClause.Occur.MUST);
		}
		
		if (StringUtils.isNotEmpty(request.getHdType())) {
			analyzerPerField.put(IndexField.FIELD_HDTYPE, new KeywordAnalyzer());
			fieldList = (String[]) ArrayUtils.add(fieldList, IndexField.FIELD_HDTYPE);
			querieList = (String[]) ArrayUtils.add(querieList, request.getHdType());
			occur = (BooleanClause.Occur[]) ArrayUtils.add(occur, BooleanClause.Occur.MUST);
		}

		// 排除指定的内容
		if (StringUtils.isNotEmpty(request.getContentCode())
				&& request.isExcludeSelf()) {
			analyzerPerField.put(IndexField.FIELD_CODE, new KeywordAnalyzer());
			fieldList = (String[]) ArrayUtils.add(fieldList, IndexField.FIELD_CODE);
			querieList = (String[]) ArrayUtils.add(querieList, request.getContentCode());
			occur = (BooleanClause.Occur[]) ArrayUtils.add(occur, BooleanClause.Occur.MUST_NOT);
		}

		PerFieldAnalyzerWrapper analyzer = new PerFieldAnalyzerWrapper(this.getSplitAnalyzer(), analyzerPerField);

		Query query = MultiFieldQueryParser.parse(DEFAULT_VERSION, querieList, fieldList, occur, analyzer);

		// 不分页查询
		if (request.getCurPage() <= 0) {
			return this.search(query, request.getPagesize());
		} else {
			return this.searchInPage(query, request.getCurPage(), request.getPagesize());
		}
	}

	/**
	 * 不分页查询.
	 * 
	 * @param query
	 * @param size
	 * @return
	 */
	private SearchResult search(Query query, int size) {
		SearchResult result = new SearchResult(size);
		
		try {
			TopDocs topDocs = searcher.search(query, size, this.buildSort());
			ScoreDoc[] hits = topDocs.scoreDocs;
			result.setTotalCount(hits.length);

			for (int i = 0; i < hits.length; i++) {
				Document hitDoc = searcher.doc(hits[i].doc);
				result.getResult().add(hitDoc.get(IndexField.FIELD_CONTENT));
			}
		} catch (IOException ex) {
			logger.error("search error.", ex);
		} 

		return result;
	}

	/**
	 * 分页查询.
	 * 
	 * @param query
	 * @param curPage
	 * @param size
	 * @return
	 */
	private SearchResult searchInPage(Query query, int curPage, int size) {
		SearchResult result = new SearchResult();
		try {
			result = this.searchInPage(query, this.buildSort(), curPage, size, null);
		} catch (IOException e) {
			logger.error("search error.", e);
		}
	
		return result;
	}
	
	private SearchResult searchInPage(Query query, Sort sort, int curPage, int size,Filter filter) throws IOException {
		SearchResult result = new SearchResult(size);
		
		int start = (curPage -1) * size;
		TopFieldCollector c = TopFieldCollector.create(sort, start+size, false, false, false, false);
		if(filter != null){
			searcher.search(query, filter, c);
		}else{
			searcher.search(query,c);
		}
		result.setTotalCount(c.getTotalHits());
		TopDocs topDocs = c.topDocs(start, size);
		ScoreDoc[] hits = topDocs.scoreDocs;
		
		for (int i = 0; i < hits.length; i++) {
			Document hitDoc = searcher.doc(hits[i].doc);
			result.getResult().add(hitDoc.get(IndexField.FIELD_CONTENT));
		}
		return result;
	}

	private Sort buildSort() {
		SortField sortFieldId = new SortField(IndexField.FIELD_ID, SortField.Type.LONG, true);
		SortField sortFieldMd = new SortField(IndexField.FIELD_SCORE, SortField.Type.FLOAT, true);
		Sort sort = new Sort(new SortField[] { SortField.FIELD_SCORE, sortFieldMd, sortFieldId });
		return sort;
	}

	public void close() {
		if (this.searcher != null && this.searcher.getIndexReader() != null) {
			try {
				this.searcher.getIndexReader().close();
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
	}

	/**
	 * 分词器.
	 * 
	 * @return
	 */
	private Analyzer getSplitAnalyzer() {
		return AnalyzerFactory.getDefaultAnalyzer();
	}
	
	public static void main(String args[]) {
		File fsDir = new File("D:/temp/search");
		try{
	        Directory ramDir = new RAMDirectory(NIOFSDirectory.open(fsDir),new IOContext());
	        IndexReader reader = DirectoryReader.open(ramDir);
	        IndexSearcher searcher = new IndexSearcher(reader);
	        
	        SearchHelper helper = new SearchHelper(searcher);
	        SearchRequest request = new SearchRequest();
	        request.setCondition("剧情");
	        request.setPagesize(1);
	        SearchResult result = helper.searchByTags(request);
            for (Object item : result.getResult()) {
                //logger.info(item.toString());
            	System.out.println(item.toString());
            }
            
            try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}catch(IOException | ParseException ex){
			
		}
	}
}
